Fedezze fel a biztonságos, eltérő eredetű kommunikációt a PostMessage API segítségével. Ismerje meg képességeit, biztonsági kockázatait és a legjobb gyakorlatokat a webalkalmazások sebezhetőségeinek csökkentésére.
Eltérő Eredetű Kommunikáció: Biztonsági Minták a PostMessage API-val
A modern weben az alkalmazásoknak gyakran kell különböző eredetű erőforrásokkal kommunikálniuk. Az Azonos Eredetű Házirend (Same-Origin Policy - SOP) egy kulcsfontosságú biztonsági mechanizmus, amely korlátozza a szkriptek hozzáférését egy másik eredetű erőforráshoz. Vannak azonban legitim forgatókönyvek, ahol az eltérő eredetű kommunikáció szükséges. A postMessage API egy ellenőrzött mechanizmust biztosít ennek eléréséhez, de elengedhetetlen a lehetséges biztonsági kockázatok megértése és a megfelelő biztonsági minták alkalmazása.
Az Azonos Eredetű Házirend (SOP) megértése
Az Azonos Eredetű Házirend egy alapvető biztonsági koncepció a webböngészőkben. Megakadályozza, hogy a weboldalak más domainhez intézzenek kéréseket, mint amelyik a weboldalt kiszolgálta. Az eredetet a séma (protokoll), a hoszt (domain) és a port határozza meg. Ha ezek bármelyike eltér, az eredeteket különbözőnek tekintjük. Például:
https://example.comhttps://www.example.comhttp://example.comhttps://example.com:8080
Ezek mind különböző eredetek, és az SOP korlátozza a közvetlen szkript-hozzáférést közöttük.
A PostMessage API bemutatása
A postMessage API egy biztonságos és ellenőrzött mechanizmust biztosít az eltérő eredetű kommunikációhoz. Lehetővé teszi, hogy a szkriptek üzeneteket küldjenek más ablakoknak (pl. iframe-eknek, új ablakoknak vagy lapoknak), azok eredetétől függetlenül. A fogadó ablak ezután figyelheti ezeket az üzeneteket és ennek megfelelően feldolgozhatja őket.
Az üzenetküldés alapvető szintaxisa:
otherWindow.postMessage(message, targetOrigin);
otherWindow: Egy hivatkozás a célablakra (pl.window.parent,iframe.contentWindow, vagy egywindow.opensegítségével kapott ablak objektum).message: Az adat, amit küldeni szeretne. Bármilyen JavaScript objektum lehet, ami szerializálható (pl. stringek, számok, objektumok, tömbök).targetOrigin: Meghatározza azt az eredetet, ahová az üzenetet küldeni szeretné. Ez egy kulcsfontosságú biztonsági paraméter.
A fogadó oldalon figyelni kell a message eseményre:
window.addEventListener('message', function(event) {
// ...
});
Az event objektum a következő tulajdonságokat tartalmazza:
event.data: A másik ablak által küldött üzenet.event.origin: Az üzenetet küldő ablak eredete.event.source: Egy hivatkozás az üzenetet küldő ablakra.
Biztonsági kockázatok és sebezhetőségek
Bár a postMessage lehetőséget kínál az SOP korlátozásainak megkerülésére, potenciális biztonsági kockázatokat is rejt magában, ha nem körültekintően implementálják. Íme néhány gyakori sebezhetőség:
1. Cél Eredet Eltérés
Az event.origin tulajdonság validálásának elmulasztása kritikus sebezhetőség. Ha a fogadó vakon megbízik az üzenetben, bármely webhely küldhet rosszindulatú adatokat. Mindig ellenőrizze, hogy az event.origin megegyezik-e a várt eredettel, mielőtt feldolgozná az üzenetet.
Példa (Sebezhető kód):
window.addEventListener('message', function(event) {
// NE TEGYE EZT!
processMessage(event.data);
});
Példa (Biztonságos kód):
window.addEventListener('message', function(event) {
if (event.origin !== 'https://trusted-origin.com') {
console.warn('Nem megbízható eredetű üzenet érkezett:', event.origin);
return;
}
processMessage(event.data);
});
2. Adat Injektálás
A kapott adat (event.data) futtatható kódként való kezelése vagy közvetlen DOM-ba való injektálása Cross-Site Scripting (XSS) sebezhetőségekhez vezethet. Mindig tisztítsa meg és validálja a kapott adatokat használat előtt.
Példa (Sebezhető kód):
window.addEventListener('message', function(event) {
if (event.origin === 'https://trusted-origin.com') {
document.body.innerHTML = event.data; // NE TEGYE EZT!
}
});
Példa (Biztonságos kód):
window.addEventListener('message', function(event) {
if (event.origin === 'https://trusted-origin.com') {
const sanitizedData = sanitize(event.data); // Implementáljon egy megfelelő tisztító függvényt
document.getElementById('message-container').textContent = sanitizedData;
}
});
function sanitize(data) {
// Itt implementáljon robusztus tisztító logikát.
// Például használja a DOMPurify-t vagy egy hasonló könyvtárat
return DOMPurify.sanitize(data);
}
3. Man-in-the-Middle (MITM) támadások
Ha a kommunikáció nem biztonságos csatornán (HTTP) keresztül történik, egy MITM támadó elfoghatja és módosíthatja az üzeneteket. A biztonságos kommunikációhoz mindig használjon HTTPS-t.
4. Cross-Site Request Forgery (CSRF)
Ha a fogadó a kapott üzenet alapján megfelelő validálás nélkül hajt végre műveleteket, egy támadó hamisított üzenetekkel ráveheti a fogadót nem szándékolt műveletek végrehajtására. Implementáljon CSRF védelmi mechanizmusokat, például egy titkos token beillesztését az üzenetbe és annak ellenőrzését a fogadó oldalon.
5. Helyettesítő karakterek (wildcardok) használata a targetOrigin-ben
A targetOrigin beállítása *-ra lehetővé teszi, hogy bármely eredet megkapja az üzenetet. Ezt kerülni kell, hacsak nem feltétlenül szükséges, mivel ez meghiúsítja az eredet alapú biztonság célját. Ha mégis *-ot kell használnia, győződjön meg róla, hogy más erős biztonsági intézkedéseket is alkalmaz, például üzenet-hitelesítő kódokat (MAC).
Példa (Kerülendő):
otherWindow.postMessage(message, '*'); // Kerülje a '*' használatát, hacsak nem feltétlenül szükséges
Biztonsági minták és legjobb gyakorlatok
A postMessage-hez kapcsolódó kockázatok csökkentése érdekében kövesse az alábbi biztonsági mintákat és legjobb gyakorlatokat:
1. Szigorú Eredetellenőrzés
Mindig validálja az event.origin tulajdonságot a fogadó oldalon. Hasonlítsa össze egy előre meghatározott, megbízható eredetek listájával. Az összehasonlításhoz használjon szigorú egyenlőséget (===).
2. Adattisztítás és -validálás
Tisztítson meg és validáljon minden postMessage-en keresztül kapott adatot, mielőtt felhasználná. Használjon megfelelő tisztítási technikákat attól függően, hogy az adatokat hogyan fogják felhasználni (pl. HTML escaping, URL kódolás, bemeneti adatok validálása). Használjon olyan könyvtárakat, mint a DOMPurify a HTML tisztítására.
3. Üzenet-hitelesítő Kódok (MAC)
Illesszen be egy Üzenet-hitelesítő Kódot (MAC) az üzenetbe annak integritásának és hitelességének biztosítása érdekében. A küldő egy megosztott titkos kulcs segítségével kiszámítja a MAC-ot, és belefoglalja az üzenetbe. A fogadó ugyanazzal a megosztott titkos kulccsal újraszámolja a MAC-ot, és összehasonlítja a kapott MAC-kal. Ha egyeznek, az üzenet hitelesnek és sértetlennek tekinthető.
Példa (HMAC-SHA256 használatával):
// Sender
async function sendMessage(message, targetOrigin, sharedSecret) {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(message));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
const securedMessage = {
data: message,
signature: signatureHex
};
otherWindow.postMessage(securedMessage, targetOrigin);
}
// Receiver
async function receiveMessage(event, sharedSecret) {
if (event.origin !== 'https://trusted-origin.com') {
console.warn('Nem megbízható eredetű üzenet érkezett:', event.origin);
return;
}
const securedMessage = event.data;
const message = securedMessage.data;
const receivedSignature = securedMessage.signature;
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(message));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (signatureHex === receivedSignature) {
console.log('Az üzenet hiteles!');
processMessage(message); // Folytassa az üzenet feldolgozását
} else {
console.error('Az üzenet aláírásának ellenőrzése sikertelen!');
}
}
Fontos: A megosztott titkos kulcsot biztonságosan kell generálni és tárolni. Kerülje a kulcs hardkódolását a kódban.
4. Nonce és időbélyeg használata
A visszajátszásos támadások (replay attack) megelőzése érdekében illesszen be egy egyedi nonce-t (egyszer használatos szám) és egy időbélyeget az üzenetbe. A fogadó ezután ellenőrizheti, hogy a nonce-t még nem használták-e, és hogy az időbélyeg egy elfogadható időkereten belül van-e. Ez csökkenti annak kockázatát, hogy egy támadó korábban elfogott üzeneteket játsszon vissza.
5. Legkisebb jogosultság elve
Csak a minimálisan szükséges jogosultságokat adja meg a másik ablaknak. Például, ha a másik ablaknak csak adatokat kell olvasnia, ne engedélyezze számára az adatok írását. A kommunikációs protokollját a legkisebb jogosultság elve alapján tervezze meg.
6. Content Security Policy (CSP)
Használjon Content Security Policy-t (CSP) annak korlátozására, hogy milyen forrásokból tölthetők be szkriptek, és milyen műveleteket hajthatnak végre a szkriptek. Ez segíthet csökkenteni a postMessage adatok nem megfelelő kezeléséből adódó XSS sebezhetőségek hatását.
7. Bemeneti adatok validálása
Mindig validálja a kapott adatok szerkezetét és formátumát. Definiáljon egy egyértelmű üzenetformátumot, és győződjön meg róla, hogy a kapott adatok megfelelnek ennek a formátumnak. Ez segít megelőzni a váratlan viselkedést és a sebezhetőségeket.
8. Biztonságos Adat Szerializáció
Használjon biztonságos adat szerializációs formátumot, például JSON-t, az üzenetek szerializálásához és deszerializálásához. Kerülje az olyan formátumok használatát, amelyek kódfuttatást tesznek lehetővé, mint például az eval() vagy a Function().
9. Üzenetméret korlátozása
Korlátozza a postMessage-en keresztül küldött üzenetek méretét. A nagy üzenetek túlzott erőforrásokat emészthetnek fel, és potenciálisan szolgáltatásmegtagadási (denial-of-service) támadásokhoz vezethetnek.
10. Rendszeres biztonsági auditok
Végezzen rendszeres biztonsági auditokat a kódján a potenciális sebezhetőségek azonosítása és kezelése érdekében. Fordítson különös figyelmet a postMessage implementációjára, és győződjön meg róla, hogy minden biztonsági legjobb gyakorlatot követnek.
Példa forgatókönyv: Biztonságos kommunikáció egy Iframe és a szülőoldala között
Vegyünk egy olyan forgatókönyvet, ahol egy https://iframe.example.com címen hosztolt iframe-nek kommunikálnia kell a szülőoldalával, amely a https://parent.example.com címen található. Az iframe-nek felhasználói adatokat kell küldenie a szülőoldalnak feldolgozásra.
Iframe (https://iframe.example.com):
// Generáljon egy megosztott titkos kulcsot (cserélje le egy biztonságos kulcsgeneráló módszerrel)
const sharedSecret = 'YOUR_SECURE_SHARED_SECRET';
// Felhasználói adatok lekérése
const userData = {
name: 'John Doe',
email: 'john.doe@example.com'
};
// Felhasználói adatok küldése a szülőoldalnak
async function sendUserData(userData) {
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(userData));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["sign"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
const securedMessage = {
data: userData,
signature: signatureHex
};
parent.postMessage(securedMessage, 'https://parent.example.com');
}
sendUserData(userData);
Szülőoldal (https://parent.example.com):
// Megosztott titkos kulcs (meg kell egyeznie az iframe kulcsával)
const sharedSecret = 'YOUR_SECURE_SHARED_SECRET';
window.addEventListener('message', async function(event) {
if (event.origin !== 'https://iframe.example.com') {
console.warn('Nem megbízható eredetű üzenet érkezett:', event.origin);
return;
}
const securedMessage = event.data;
const userData = securedMessage.data;
const receivedSignature = securedMessage.signature;
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify(userData));
const key = await crypto.subtle.importKey(
"raw",
encoder.encode(sharedSecret),
{ name: "HMAC", hash: "SHA-256" },
false,
["verify"]
);
const signature = await crypto.subtle.sign("HMAC", key, data);
const signatureArray = Array.from(new Uint8Array(signature));
const signatureHex = signatureArray.map(b => b.toString(16).padStart(2, '0')).join('');
if (signatureHex === receivedSignature) {
console.log('Az üzenet hiteles!');
// A felhasználói adatok feldolgozása
console.log('Felhasználói adatok:', userData);
} else {
console.error('Az üzenet aláírásának ellenőrzése sikertelen!');
}
});
Fontos megjegyzések:
- Cserélje le a
YOUR_SECURE_SHARED_SECRETrészt egy biztonságosan generált megosztott titkos kulcsra. - A megosztott titkos kulcsnak ugyanannak kell lennie mind az iframe-ben, mind a szülőoldalon.
- Ez a példa HMAC-SHA256-ot használ az üzenet hitelesítésére.
Összegzés
A postMessage API egy hatékony eszköz az eltérő eredetű kommunikáció lehetővé tételére a webalkalmazásokban. Azonban kulcsfontosságú megérteni a lehetséges biztonsági kockázatokat és megfelelő biztonsági mintákat alkalmazni ezek csökkentésére. Az ebben az útmutatóban felvázolt biztonsági minták és legjobb gyakorlatok követésével biztonságosan használhatja a postMessage-t robusztus és biztonságos webalkalmazások építéséhez.
Ne feledje, hogy mindig a biztonságot kell előtérbe helyeznie, és naprakésznek kell lennie a webfejlesztés legújabb biztonsági legjobb gyakorlataival. Rendszeresen vizsgálja felül kódját és biztonsági konfigurációit, hogy megbizonyosodjon arról, hogy alkalmazásai védettek a lehetséges sebezhetőségekkel szemben.